## [1] "wx_temperature" "wx_wind_speed" "wx_precipitation"
## [4] "localizacion" "distrito" "tipo_accidente"
## [7] "estado_meteorol_gico" "tipo_vehiculo" "tipo_persona"
## [10] "rango_edad" "sexo" "lesividad"
## [13] "coordenada_x_utm" "coordenada_y_utm" "positiva_alcohol"
## [16] "positiva_droga" "time"
## 'data.frame': 221910 obs. of 17 variables:
## $ wx_temperature : num -1 -1 1.9 1.9 1.9 1.9 1.9 1.9 1.9 1.9 ...
## $ wx_wind_speed : num 1.02 1.02 0.73 0.73 0.73 0.73 0.73 0.73 0.73 0.73 ...
## $ wx_precipitation : num 0 0 0 0 0 0 0 0 0 0 ...
## $ localizacion : chr "CALL. ALBERTO AGUILERA, 1" "CALL. ALBERTO AGUILERA, 1" "PASEO. SANTA MARIA DE LA CABEZA / PLAZA. ELIPTICA" "PASEO. SANTA MARIA DE LA CABEZA / PLAZA. ELIPTICA" ...
## $ distrito : chr "CENTRO" "CENTRO" "CARABANCHEL" "CARABANCHEL" ...
## $ tipo_accidente : chr "Colisión lateral" "Colisión lateral" "Alcance" "Alcance" ...
## $ estado_meteorol_gico: chr "Despejado" "Despejado" "No se registró" "No se registró" ...
## $ tipo_vehiculo : chr "Motocicleta > 125cc" "Turismo" "Furgoneta" "Turismo" ...
## $ tipo_persona : chr "Conductor" "Conductor" "Conductor" "Conductor" ...
## $ rango_edad : chr "De 45 a 49 años" "De 30 a 34 años" "De 40 a 44 años" "De 40 a 44 años" ...
## $ sexo : chr "Hombre" "Mujer" "Hombre" "Mujer" ...
## $ lesividad : chr "Asistencia sanitaria sólo en el lugar del accidente" "Asistencia sanitaria sólo en el lugar del accidente" "No se registró" "No se registró" ...
## $ coordenada_x_utm : int 440068049 440068049 439139603 439139603 439139603 439139603 439139603 439139603 436473789 436473789 ...
## $ coordenada_y_utm : num 4.48e+08 4.48e+08 4.47e+09 4.47e+09 4.47e+09 ...
## $ positiva_alcohol : chr "N" "N" "S" "N" ...
## $ positiva_droga : int 0 0 0 0 0 0 0 0 0 0 ...
## $ time : chr "2019-02-04 09:10" "2019-02-04 09:10" "2019-01-01 03:45" "2019-01-01 03:45" ...
## [1] "Asistencia sanitaria sólo en el lugar del accidente"
## [2] "No se registró"
## [3] "Ingreso inferior o igual a 24 horas"
## [4] "Sin asistencia sanitaria"
## [5] "Asistencia sanitaria ambulatoria con posterioridad"
## [6] "Ingreso superior a 24 horas"
## [7] "Atención en urgencias sin posterior ingreso"
## [8] "Asistencia sanitaria inmediata en centro de salud o mutua"
## [9] "Fallecido 24 horas"
## [10] "Se desconoce"
## [11] ""
# Estandarizar valores faltantes y limpiar espacios en blanco
accidents_clean_data <- accidents_clean_data %>%
# Eliminar espacios en blanco en columnas de texto
mutate(across(where(is.character), ~str_trim(.))) %>%
# Reemplazar valores problemáticos por NA
mutate(across(where(is.character),
~case_when(
. %in% c("", "NA", "N/A", "No se registró", "No se registro",
"null", "NULL", "Sin dato", "Desconocido") ~ NA_character_,
TRUE ~ .
)))# Calcular porcentaje de valores faltantes por columna
missing_summary <- accidents_clean_data %>%
summarise(across(everything(), ~mean(is.na(.)) * 100)) %>%
pivot_longer(cols = everything(), names_to = "columna", values_to = "porcentaje_na") %>%
arrange(desc(porcentaje_na))
# Mostrar resumen de valores faltantes
print(missing_summary)## # A tibble: 17 × 2
## columna porcentaje_na
## <chr> <dbl>
## 1 lesividad 45.3
## 2 wx_temperature 41.7
## 3 wx_wind_speed 41.7
## 4 wx_precipitation 41.7
## 5 rango_edad 11.0
## 6 estado_meteorol_gico 10.7
## 7 sexo 10.7
## 8 tipo_vehiculo 0.428
## 9 positiva_alcohol 0.355
## 10 coordenada_x_utm 0.0194
## 11 coordenada_y_utm 0.0194
## 12 distrito 0.00586
## 13 tipo_accidente 0.00406
## 14 tipo_persona 0.00361
## 15 positiva_droga 0.00225
## 16 localizacion 0
## 17 time 0
# Contar valores únicos en columnas categóricas
sapply(accidents_clean_data[, sapply(accidents_clean_data, is.character)],
function(x) length(unique(x)))## localizacion distrito tipo_accidente
## 50365 22 14
## estado_meteorol_gico tipo_vehiculo tipo_persona
## 8 41 4
## rango_edad sexo lesividad
## 18 3 10
## positiva_alcohol time
## 3 85127
# Extraer componentes de fecha y tiempo
accidents_clean_data$fecha <- as.Date(accidents_clean_data$time)
accidents_clean_data$anio <- year(accidents_clean_data$fecha)
accidents_clean_data$hora <- hour(accidents_clean_data$time)
accidents_clean_data$dia_semana <- wday(accidents_clean_data$time, label = TRUE, abbr = FALSE)
accidents_clean_data$mes <- month(accidents_clean_data$time, label = TRUE)
# Crear variable de estación del año
accidents_clean_data$estacion <- case_when(
accidents_clean_data$mes %in% c("diciembre", "enero", "febrero") ~ "Invierno",
accidents_clean_data$mes %in% c("marzo", "abril", "mayo") ~ "Primavera",
accidents_clean_data$mes %in% c("junio", "julio", "agosto") ~ "Verano",
accidents_clean_data$mes %in% c("septiembre", "octubre", "noviembre") ~ "Otoño"
)# Clasificar accidentes por franja horaria
accidents_clean_data$franja_horaria <- case_when(
accidents_clean_data$hora >= 6 & accidents_clean_data$hora < 12 ~ "Mañana",
accidents_clean_data$hora >= 12 & accidents_clean_data$hora < 18 ~ "Tarde",
accidents_clean_data$hora >= 18 & accidents_clean_data$hora < 24 ~ "Noche",
TRUE ~ "Madrugada"
)
# Clasificar fin de semana vs entre semana
accidents_clean_data$es_fin_de_semana <- ifelse(
accidents_clean_data$dia_semana %in% c("sábado", "domingo"), "Sí", "No"
)# Categorizar temperatura en rangos
accidents_clean_data$categoria_temp <- case_when(
accidents_clean_data$wx_temperature < 5 ~ "Frío (< 5°C)",
accidents_clean_data$wx_temperature >= 5 & accidents_clean_data$wx_temperature < 15 ~ "Templado (5-15°C)",
accidents_clean_data$wx_temperature >= 15 & accidents_clean_data$wx_temperature < 25 ~ "Cálido (15-25°C)",
accidents_clean_data$wx_temperature >= 25 ~ "Caluroso (> 25°C)"
)
# Crear indicador binario de precipitación
accidents_clean_data$hubo_lluvia <- ifelse(accidents_clean_data$wx_precipitation > 0, "Sí", "No")# Crear variable de gravedad simplificada
accidents_clean_data <- accidents_clean_data %>%
mutate(gravedad_binaria = case_when(
# Casos leves: atención en el lugar, sin asistencia o ambulatoria
grepl("sólo en el lugar|Sin asistencia|ambulatoria", lesividad, ignore.case = TRUE) ~ "Leve",
# Casos graves: ingreso hospitalario, urgencias, fallecimientos
grepl("Ingreso|urgencias|centro de salud|mutua|Fallecido", lesividad, ignore.case = TRUE) ~ "Grave",
# Casos sin información
lesividad == "Se desconoce" | is.na(lesividad) ~ "Desconocido",
# Otros casos no clasificados
TRUE ~ "Otro"
))## 'data.frame': 221910 obs. of 28 variables:
## $ wx_temperature : num -1 -1 1.9 1.9 1.9 1.9 1.9 1.9 1.9 1.9 ...
## $ wx_wind_speed : num 1.02 1.02 0.73 0.73 0.73 0.73 0.73 0.73 0.73 0.73 ...
## $ wx_precipitation : num 0 0 0 0 0 0 0 0 0 0 ...
## $ localizacion : chr "CALL. ALBERTO AGUILERA, 1" "CALL. ALBERTO AGUILERA, 1" "PASEO. SANTA MARIA DE LA CABEZA / PLAZA. ELIPTICA" "PASEO. SANTA MARIA DE LA CABEZA / PLAZA. ELIPTICA" ...
## $ distrito : chr "CENTRO" "CENTRO" "CARABANCHEL" "CARABANCHEL" ...
## $ tipo_accidente : chr "Colisión lateral" "Colisión lateral" "Alcance" "Alcance" ...
## $ estado_meteorol_gico: chr "Despejado" "Despejado" NA NA ...
## $ tipo_vehiculo : chr "Motocicleta > 125cc" "Turismo" "Furgoneta" "Turismo" ...
## $ tipo_persona : chr "Conductor" "Conductor" "Conductor" "Conductor" ...
## $ rango_edad : chr "De 45 a 49 años" "De 30 a 34 años" "De 40 a 44 años" "De 40 a 44 años" ...
## $ sexo : chr "Hombre" "Mujer" "Hombre" "Mujer" ...
## $ lesividad : chr "Asistencia sanitaria sólo en el lugar del accidente" "Asistencia sanitaria sólo en el lugar del accidente" NA NA ...
## $ coordenada_x_utm : int 440068049 440068049 439139603 439139603 439139603 439139603 439139603 439139603 436473789 436473789 ...
## $ coordenada_y_utm : num 4.48e+08 4.48e+08 4.47e+09 4.47e+09 4.47e+09 ...
## $ positiva_alcohol : chr "N" "N" "S" "N" ...
## $ positiva_droga : int 0 0 0 0 0 0 0 0 0 0 ...
## $ time : chr "2019-02-04 09:10" "2019-02-04 09:10" "2019-01-01 03:45" "2019-01-01 03:45" ...
## $ fecha : Date, format: "2019-02-04" "2019-02-04" ...
## $ anio : num 2019 2019 2019 2019 2019 ...
## $ hora : int 9 9 3 3 3 3 3 3 3 3 ...
## $ dia_semana : Ord.factor w/ 7 levels "Sunday"<"Monday"<..: 2 2 3 3 3 3 3 3 3 3 ...
## $ mes : Ord.factor w/ 12 levels "Jan"<"Feb"<"Mar"<..: 2 2 1 1 1 1 1 1 1 1 ...
## $ estacion : chr NA NA NA NA ...
## $ franja_horaria : chr "Mañana" "Mañana" "Madrugada" "Madrugada" ...
## $ es_fin_de_semana : chr "No" "No" "No" "No" ...
## $ categoria_temp : chr "Frío (< 5°C)" "Frío (< 5°C)" "Frío (< 5°C)" "Frío (< 5°C)" ...
## $ hubo_lluvia : chr "No" "No" "No" "No" ...
## $ gravedad_binaria : chr "Leve" "Leve" "Desconocido" "Desconocido" ...
## wx_temperature wx_wind_speed wx_precipitation localizacion
## Min. :-6.40 Min. :0.000 Min. : 0.000 Length:221910
## 1st Qu.: 7.30 1st Qu.:1.150 1st Qu.: 0.000 Class :character
## Median :12.10 Median :1.850 Median : 0.000 Mode :character
## Mean :13.69 Mean :2.145 Mean : 0.061
## 3rd Qu.:19.80 3rd Qu.:2.900 3rd Qu.: 0.000
## Max. :37.90 Max. :9.450 Max. :27.400
## NA's :92427 NA's :92427 NA's :92427
## distrito tipo_accidente estado_meteorol_gico tipo_vehiculo
## Length:221910 Length:221910 Length:221910 Length:221910
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## tipo_persona rango_edad sexo lesividad
## Length:221910 Length:221910 Length:221910 Length:221910
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## coordenada_x_utm coordenada_y_utm positiva_alcohol positiva_droga
## Min. : 0 Min. :0.000e+00 Length:221910 Min. :0.0000
## 1st Qu.:439335254 1st Qu.:4.471e+09 Class :character 1st Qu.:0.0000
## Median :441506946 Median :4.474e+09 Mode :character Median :0.0000
## Mean :410683724 Mean :4.161e+09 Mean :0.0031
## 3rd Qu.:443925885 3rd Qu.:4.477e+09 3rd Qu.:0.0000
## Max. :455483886 Max. :4.496e+09 Max. :1.0000
## NA's :43 NA's :43 NA's :5
## time fecha anio hora
## Length:221910 Min. :2019-01-01 Min. :2019 Min. : 0.00
## Class :character 1st Qu.:2020-01-29 1st Qu.:2020 1st Qu.:10.00
## Mode :character Median :2021-09-18 Median :2021 Median :15.00
## Mean :2021-07-21 Mean :2021 Mean :14.07
## 3rd Qu.:2022-11-16 3rd Qu.:2022 3rd Qu.:19.00
## Max. :2023-12-31 Max. :2023 Max. :23.00
##
## dia_semana mes estacion franja_horaria
## Sunday :26266 Oct : 21811 Length:221910 Length:221910
## Monday :29795 Dec : 21497 Class :character Class :character
## Tuesday :32493 Nov : 21342 Mode :character Mode :character
## Wednesday:32900 Jun : 19219
## Thursday :33563 Sep : 18860
## Friday :37298 Feb : 18662
## Saturday :29595 (Other):100519
## es_fin_de_semana categoria_temp hubo_lluvia gravedad_binaria
## Length:221910 Length:221910 Length:221910 Length:221910
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## wx_temperature wx_wind_speed wx_precipitation
## 92427 92427 92427
## localizacion distrito tipo_accidente
## 0 13 9
## estado_meteorol_gico tipo_vehiculo tipo_persona
## 23750 949 8
## rango_edad sexo lesividad
## 24425 23664 100571
## coordenada_x_utm coordenada_y_utm positiva_alcohol
## 43 43 787
## positiva_droga time fecha
## 5 0 0
## anio hora dia_semana
## 0 0 0
## mes estacion franja_horaria
## 0 221910 0
## es_fin_de_semana categoria_temp hubo_lluvia
## 0 92427 92427
## gravedad_binaria
## 0
## wx_temperature wx_wind_speed wx_precipitation
## 1 -1.0 1.02 0
## 2 -1.0 1.02 0
## 3 1.9 0.73 0
## 4 1.9 0.73 0
## 5 1.9 0.73 0
## 6 1.9 0.73 0
## localizacion distrito
## 1 CALL. ALBERTO AGUILERA, 1 CENTRO
## 2 CALL. ALBERTO AGUILERA, 1 CENTRO
## 3 PASEO. SANTA MARIA DE LA CABEZA / PLAZA. ELIPTICA CARABANCHEL
## 4 PASEO. SANTA MARIA DE LA CABEZA / PLAZA. ELIPTICA CARABANCHEL
## 5 PASEO. SANTA MARIA DE LA CABEZA / PLAZA. ELIPTICA CARABANCHEL
## 6 PASEO. SANTA MARIA DE LA CABEZA / PLAZA. ELIPTICA CARABANCHEL
## tipo_accidente estado_meteorol_gico tipo_vehiculo tipo_persona
## 1 Colisión lateral Despejado Motocicleta > 125cc Conductor
## 2 Colisión lateral Despejado Turismo Conductor
## 3 Alcance <NA> Furgoneta Conductor
## 4 Alcance <NA> Turismo Conductor
## 5 Alcance <NA> Turismo Conductor
## 6 Alcance <NA> Turismo Pasajero
## rango_edad sexo lesividad
## 1 De 45 a 49 años Hombre Asistencia sanitaria sólo en el lugar del accidente
## 2 De 30 a 34 años Mujer Asistencia sanitaria sólo en el lugar del accidente
## 3 De 40 a 44 años Hombre <NA>
## 4 De 40 a 44 años Mujer <NA>
## 5 De 45 a 49 años Mujer <NA>
## 6 De 45 a 49 años Mujer <NA>
## coordenada_x_utm coordenada_y_utm positiva_alcohol positiva_droga
## 1 440068049 447567917 N 0
## 2 440068049 447567917 N 0
## 3 439139603 4470836854 S 0
## 4 439139603 4470836854 N 0
## 5 439139603 4470836854 N 0
## 6 439139603 4470836854 N 0
## time fecha anio hora dia_semana mes estacion franja_horaria
## 1 2019-02-04 09:10 2019-02-04 2019 9 Monday Feb <NA> Mañana
## 2 2019-02-04 09:10 2019-02-04 2019 9 Monday Feb <NA> Mañana
## 3 2019-01-01 03:45 2019-01-01 2019 3 Tuesday Jan <NA> Madrugada
## 4 2019-01-01 03:45 2019-01-01 2019 3 Tuesday Jan <NA> Madrugada
## 5 2019-01-01 03:45 2019-01-01 2019 3 Tuesday Jan <NA> Madrugada
## 6 2019-01-01 03:45 2019-01-01 2019 3 Tuesday Jan <NA> Madrugada
## es_fin_de_semana categoria_temp hubo_lluvia gravedad_binaria
## 1 No Frío (< 5°C) No Leve
## 2 No Frío (< 5°C) No Leve
## 3 No Frío (< 5°C) No Desconocido
## 4 No Frío (< 5°C) No Desconocido
## 5 No Frío (< 5°C) No Desconocido
## 6 No Frío (< 5°C) No Desconocido
## [1] "wx_temperature" "wx_wind_speed" "wx_precipitation"
## [4] "localizacion" "distrito" "tipo_accidente"
## [7] "estado_meteorol_gico" "tipo_vehiculo" "tipo_persona"
## [10] "rango_edad" "sexo" "lesividad"
## [13] "coordenada_x_utm" "coordenada_y_utm" "positiva_alcohol"
## [16] "positiva_droga" "time" "fecha"
## [19] "anio" "hora" "dia_semana"
## [22] "mes" "estacion" "franja_horaria"
## [25] "es_fin_de_semana" "categoria_temp" "hubo_lluvia"
## [28] "gravedad_binaria"
accidents_clean_data %>%
filter(!is.na(tipo_accidente)) %>%
count(tipo_accidente) %>%
mutate(tipo_accidente = reorder(tipo_accidente, n)) %>%
ggplot(aes(x = tipo_accidente, y = n)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = n), hjust = -0.1) +
coord_flip() +
labs(
title = "Distribución de tipos de accidente en Madrid (2019–2023)",
x = "Tipo de accidente",
y = "Número de accidentes"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
axis.text.y = element_text(size = 9)
)# Calcular total de accidentes por distrito
accidentes_por_distrito <- accidents_clean_data %>%
filter(!is.na(distrito)) %>%
group_by(distrito) %>%
summarise(total_accidentes = n()) %>%
arrange(desc(total_accidentes))
# Visualización
ggplot(accidentes_por_distrito,
aes(x = reorder(distrito, total_accidentes), y = total_accidentes, fill = distrito)) +
geom_col() +
geom_text(aes(label = total_accidentes), vjust = 0.5, hjust = -0.1) +
coord_flip() +
labs(
title = "Accidentes de tráfico por distrito (Madrid 2019–2023)",
x = "Distrito",
y = "Número de accidentes"
) +
theme_minimal() +
theme(legend.position = "none")# Calcular accidentes por tipo de vehículo
accidentes_por_tipo_vehiculo <- accidents_clean_data %>%
filter(!is.na(tipo_vehiculo)) %>%
group_by(tipo_vehiculo) %>%
summarise(total_accidentes = n()) %>%
arrange(desc(total_accidentes))
# Visualización
ggplot(accidentes_por_tipo_vehiculo,
aes(x = reorder(tipo_vehiculo, total_accidentes), y = total_accidentes)) +
geom_col(fill = "tomato") +
coord_flip() +
labs(
title = "Accidentes de tráfico por tipo de vehículo (Madrid 2019–2023)",
x = "Tipo de vehículo",
y = "Número de accidentes"
) +
theme_minimal()# Filtrar datos sin NA en tipo de accidente
accidents_clean_data_sin_na <- accidents_clean_data %>%
filter(!is.na(tipo_accidente))
# Visualización de proporción de gravedad por tipo de accidente
ggplot(accidents_clean_data_sin_na, aes(x = tipo_accidente, fill = gravedad_binaria)) +
geom_bar(position = "fill") +
coord_flip() +
labs(
title = "Gravedad según tipo de accidente",
x = "Tipo de accidente",
y = "Proporción"
) +
theme_minimal()# Contar accidentes por estado meteorológico
accidentes_clima <- accidents_clean_data %>%
filter(!is.na(estado_meteorol_gico)) %>%
count(estado_meteorol_gico) %>%
arrange(desc(n))
# Visualización
ggplot(accidentes_clima, aes(x = reorder(estado_meteorol_gico, n), y = n)) +
geom_col(fill = "skyblue") +
geom_text(aes(label = n), hjust = -0.1) +
coord_flip() +
labs(
title = "Accidentes según estado meteorológico (Madrid 2019–2023)",
x = "Estado meteorológico",
y = "Número de accidentes"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
axis.text.y = element_text(size = 10)
)# Calcular media de accidentes por día según condición meteorológica
accidentes_clima <- accidents_clean_data %>%
filter(!is.na(estado_meteorol_gico)) %>%
mutate(fecha = as.Date(time)) %>%
group_by(estado_meteorol_gico, fecha) %>%
summarise(accidentes_dia = n(), .groups = 'drop') %>%
group_by(estado_meteorol_gico) %>%
summarise(media = mean(accidentes_dia)) %>%
arrange(desc(media))
# Visualización
ggplot(accidentes_clima, aes(x = reorder(estado_meteorol_gico, media), y = media)) +
geom_col(fill = "grey") +
geom_text(aes(label = round(media, 1)), hjust = -0.1) +
coord_flip() +
labs(
title = "Media de accidentes por día según estado meteorológico (Madrid 2019–2023)",
x = "Estado meteorológico",
y = "Media de accidentes por día"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
axis.text.y = element_text(size = 10)
)# Filtrar datos con temperatura categorizada
accidents_clean_data_temp <- accidents_clean_data %>%
filter(!is.na(categoria_temp))
# Visualización
ggplot(accidents_clean_data_temp, aes(x = categoria_temp)) +
geom_bar(fill = "orange") +
labs(
title = "Accidentes según categoría de temperatura",
x = "Temperatura",
y = "Número de accidentes"
) +
theme_minimal()# Filtrar datos con información de lluvia
accidents_clean_data_lluvia <- accidents_clean_data %>%
filter(!is.na(hubo_lluvia))
# Visualización
ggplot(accidents_clean_data_lluvia, aes(x = hubo_lluvia)) +
geom_bar(fill = "dodgerblue") +
labs(
title = "Accidentes según presencia de lluvia",
x = "¿Llovió?",
y = "Número de accidentes"
) +
theme_minimal()# Calcular accidentes por año
accidentes_por_anio <- accidents_clean_data %>%
group_by(anio) %>%
summarise(total_accidentes = n())
# Visualización
ggplot(accidentes_por_anio, aes(x = factor(anio), y = total_accidentes)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = total_accidentes), vjust = -0.5, size = 4) +
labs(
title = "Número de accidentes por año en Madrid (2019–2023)",
x = "Año",
y = "Número de accidentes"
) +
theme_minimal()# Filtrar y normalizar coordenadas UTM
accidents_clean_data_filtered <- accidents_clean_data %>%
filter(!is.na(coordenada_x_utm) & !is.na(coordenada_y_utm)) %>%
mutate(
coordenada_x_utm = as.numeric(coordenada_x_utm) / 1000,
coordenada_y_utm = as.numeric(coordenada_y_utm) / 1000
) %>%
# Filtrar coordenadas válidas para el área metropolitana de Madrid (UTM zona 30N)
filter(
coordenada_x_utm >= 430000 & coordenada_x_utm <= 450000,
coordenada_y_utm >= 4465000 & coordenada_y_utm <= 4485000
)
# Convertir a objeto espacial SF con proyección UTM
accidentes_sf <- st_as_sf(accidents_clean_data_filtered,
coords = c("coordenada_x_utm", "coordenada_y_utm"),
crs = 25830)
# Transformar a sistema WGS84 (latitud/longitud)
accidentes_sf <- st_transform(accidentes_sf, crs = 4326)# Cargar shapefile de los distritos de Madrid
madrid_sf <- st_read("../data/raw/distritos/DISTRITOS.shp")## Reading layer `DISTRITOS' from data source
## `/Users/ditmarestradabernuy/Yachay/UPV/Data Science/DASproject/data/raw/distritos/DISTRITOS.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 21 features and 11 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: 424753.5 ymin: 4462566 xmax: 456033.3 ymax: 4499366
## Projected CRS: ETRS89 / UTM zone 30N
# Crear mapa de calor superpuesto sobre límites de distritos
ggplot() +
# Capa base: límites administrativos de distritos
geom_sf(data = madrid_sf, fill = NA, color = "gray40", size = 0.3) +
# Capa de densidad: mapa de calor de accidentes
stat_density_2d(
data = accidents_clean_data_filtered,
aes(x = coordenada_x_utm, y = coordenada_y_utm, fill = ..level..),
geom = "polygon",
alpha = 0.6
) +
# Escala de color de amarillo (baja densidad) a rojo (alta densidad)
scale_fill_gradient(low = "yellow", high = "red", name = "Densidad") +
# Títulos y etiquetas
labs(
title = "Mapa de calor de accidentes en Madrid",
subtitle = "Superpuesto sobre distritos (2019-2023)",
x = "Coordenada X (UTM)",
y = "Coordenada Y (UTM)",
caption = "Fuente: Datos Abiertos Madrid"
) +
# Estilo visual
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
plot.subtitle = element_text(hjust = 0.5, size = 10),
legend.position = "right"
)# Normalizar nombres de distrito para evitar problemas de coincidencia
madrid_sf$NOMBRE <- toupper(trimws(madrid_sf$NOMBRE))
accidents_clean_data_filtered$distrito <- toupper(trimws(accidents_clean_data_filtered$distrito))
# Calcular centroides geométricos de cada distrito
centroides_sf <- st_point_on_surface(madrid_sf)
coords <- st_coordinates(centroides_sf)
# Contar accidentes por distrito
accidentes_distrito <- accidents_clean_data_filtered %>%
count(distrito, name = "n_accidentes")
# Preparar datos para visualización con coordenadas de centroides
datos_mapa <- data.frame(
distrito = madrid_sf$NOMBRE,
x = coords[,1],
y = coords[,2]
) %>%
left_join(accidentes_distrito, by = c("distrito" = "distrito")) %>%
mutate(n_accidentes = ifelse(is.na(n_accidentes), 0, n_accidentes))
# Crear mapa con burbujas proporcionales al número de accidentes
ggplot() +
# Capa base: polígonos de distritos
geom_sf(data = madrid_sf, fill = "gray95", color = "gray50", size = 0.3) +
# Burbujas: tamaño proporcional al número de accidentes
geom_point(data = datos_mapa,
aes(x = x, y = y, size = n_accidentes, color = n_accidentes),
alpha = 0.7) +
# Escalas de color y tamaño
scale_color_gradient(low = "green", high = "#de2d27", name = "Accidentes") +
scale_size_continuous(range = c(3, 20), name = "Accidentes", labels = comma) +
# Etiquetas de nombre de distrito
geom_text(data = datos_mapa,
aes(x = x, y = y, label = distrito),
size = 2.5, color = "black", vjust = 1.2) +
# Títulos y metadatos
labs(
title = "Cantidad de accidentes por distrito",
subtitle = "Madrid 2019–2023",
caption = "Fuente: Datos Abiertos Madrid"
) +
# Estilo visual limpio
theme_void() +
theme(
plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
plot.subtitle = element_text(hjust = 0.5, size = 10),
legend.position = "right"
)ggplot(accidents_clean_data, aes(x = rango_edad, fill = sexo)) +
geom_bar(position = "dodge") +
coord_flip() +
labs(
title = "Distribución de accidentes por rango de edad y sexo",
x = "Rango de edad",
y = "Número de accidentes",
fill = "Sexo"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
axis.text.y = element_text(size = 10)
)# Seleccionar únicamente variables meteorológicas numéricas
numericas <- accidents_clean_data[, c("wx_temperature", "wx_wind_speed", "wx_precipitation")]
# Calcular matriz de correlación
cor_matrix <- cor(numericas, use = "complete.obs")
# Visualizar matriz de correlaciones
corrplot(cor_matrix, method = "color")# Preparar dataset con solo coordenadas espaciales
datos_geo <- accidents_clean_data_filtered[, c("coordenada_x_utm", "coordenada_y_utm")]
# Aplicar algoritmo K-means con 5 clusters
set.seed(123)
km_result <- kmeans(datos_geo, centers = 5, nstart = 25)
# Añadir identificador de cluster al dataset
accidents_clean_data_filtered$cluster_zona <- as.factor(km_result$cluster)
# Visualizar clusters identificados
fviz_cluster(km_result, data = datos_geo,
palette = "jco",
ggtheme = theme_minimal(),
main = "Clustering de zonas con accidentes")Información del Análisis